#include <cstdlib>
#include <iostream>
#include <string>

#include <boost/bind.hpp>
#include <boost/system/system_error.hpp>

#include <boost/context.hpp>

#define print(msg)\
        std::cout << msg << std::endl;



        

template <class F>
struct sc_thread{
        boost::context main_;
        boost::context active_;

        sc_thread(F f)
              : main_(boost::context::current())
              , active_(boost::context::create(f,main_,this,boost::context::default_stacksize)) 
        {}
        
        void resume(){
                print("\\\\\\\\in resume before");
                main_.jump_to(active_);
                print("////in resume after");
        }
        
        void suspend(){
                print("\\\\\\\\in suspend before");
                active_.jump_to(main_);
                print("////in suspend after");
        }
};

void f(void * vp){
        int x;
        while(x<20){
                x++;
                print(">>>>X is:" << x);
                ((sc_thread<void (*)(void*)>*)vp)->suspend();
        }
        print("got to after the while");
}

void g(void * vp){
        int y=5;
        while(y>0){
                y--;
                print("<<<<Y is:" << y);
                ((sc_thread<void (*)(void*)>*)vp)->suspend();
        }
}

int main(int argc,char** argv){
        print("main() started");
        sc_thread<void (*)(void*)> t(&f);
        sc_thread<void (*)(void*)> r(&g);

        print("main is doing its things..");
        print("main calls the thread");
        t.resume();
        print("main does more things and calls the thread once more");
        t.resume();
        print("calls 10 times");
        for(int i=0; i<10;i++){ t.resume(); }
        print("now make some calls to g() thread");
        for(int i=0; i<3;i++){ r.resume(); }
        print("calls untill f it exits to the main context");
        for(int i=0; i<9;i++){ t.resume(); } //10 is segfault
        print("also g");
        for(int i=0; i<2;i++){ r.resume(); }

        print("Bye! from main");
        return 0;
}
